package ar.edu.utn.frba.dds.tp.code.vuelos;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

import org.uqbar.commons.model.Entity;

import uqbar.arena.persistence.annotations.PersistentClass;
import uqbar.arena.persistence.annotations.PersistentField;
import uqbar.arena.persistence.annotations.Relation;

import ar.edu.utn.frba.dds.tp.code.Asientos.AsientoDeVuelo;
import ar.edu.utn.frba.dds.tp.code.Asientos.EstadoAsiento;
import ar.edu.utn.frba.dds.tp.code.adapter.AbstractConversor;
import ar.edu.utn.frba.dds.tp.code.transacciones.Transaccion;
import ar.edu.utn.frba.dds.tp.code.usuarios.Usuario;
import ar.edu.utn.frba.dds.tp.code.usuarios.UsuarioStandard;
import ar.edu.utn.frba.dds.tp.homes.AerolineasHome;
import ar.edu.utn.frba.dds.tp.homes.AsientosHome;
import ar.edu.utn.frba.dds.tp.homes.TransaccionesHome;
import ar.edu.utn.frba.dds.tp.homes.VuelosHome;

import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;

@PersistentClass
public abstract class Aerolinea extends Entity{
	
	private static final long serialVersionUID = 1L;

	private String name;
	protected ArrayList<Vuelo> vuelos = new ArrayList<Vuelo>();

	// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	// +++ Constructor ++++++++++++++++++++++++++++++++++++++++++++
	// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	
	public Aerolinea(){
		BuscadorVuelos elBuscador = BuscadorVuelos.getInstance();
		elBuscador.agregarAerolinea(this);
		AerolineasHome.getInstance().crearSiCorresponde(this);
	}

	protected Aerolinea(Aerolinea aero) {
		this.setName(aero.getName());
	}
	// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	// +++ Abstract Methods +++++++++++++++++++++++++++++++++++++++
	// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	
	public abstract void comprarAsiento(AsientoDeVuelo asiento, Usuario comprador);
	public abstract List<Vuelo> vuelosDisponibles(Busqueda busqueda);
	
	public abstract AbstractConversor getConversor();
	
	// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	// +++ Persistent Fields ++++++++++++++++++++++++++++++++++++++
	// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	
	@PersistentField
	public String getName() {
		return this.name;
	}
	
	public void setName (String name) {
		this.name = name;
	}

	@Relation
	public ArrayList<Vuelo> getVuelos() {
		return this.vuelos;
	}
	
	public void setVuelos (ArrayList<Vuelo> vuelos) {
		this.vuelos = vuelos;
	}
	
	// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	// +++ Business Methods +++++++++++++++++++++++++++++++++++++++
	// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	
	public BigDecimal getImpuesto(){
		return new BigDecimal("0.00");
	}
	
	public void reservarAsiento(AsientoDeVuelo asiento, UsuarioStandard reservante){
		throw new TransactionCouldntSucceedException("La aerolinea no soporta reservas");
	}
	
	public  void handlerNuevaBusqueda(Busqueda busqueda){
		busqueda.addVuelos(this.vuelosDisponibles(busqueda));
	}

	public void update(AsientoDeVuelo asiento) {
		AsientosHome.getInstance().update(asiento);
	}
		
	protected void transferirReserva(AsientoDeVuelo asiento) {
		Vuelo vuelo = buscarVuelo(asiento);
		asiento = buscarAsiento(asiento, vuelo);
		agregarAsientoSiCorresponde(asiento, vuelo);
		UsuarioStandard reservante = asiento.primerSobreReservante();
		
		if(reservante != null){ //TODO: cuando modifique primerSobreReservante esto hay que cambiar tambien
			reservarAsiento(asiento, reservante);
		}
		else{
			asiento.setEstado(EstadoAsiento.DISPONIBLE);
			throw new TransactionCouldntSucceedException("No existen reservantes");
		}
	}

	protected void ponerEnVuelo(AsientoDeVuelo asiento, ArrayList<Vuelo> vuelos) {
		if (!asiento.getEstado().equals(EstadoAsiento.COMPRADO)){
			Vuelo vuelo = buscarVuelo(asiento);
			vuelo.setAerolinea(this);
			asiento = buscarAsiento(asiento, vuelo);
			agregarAsientoSiCorresponde(asiento, vuelo);
			this.agregarVueloSiCorresponde(vuelo, vuelos);
		}
		// TODO delegar esto en el estado.
	}

	private void agregarAsientoSiCorresponde(AsientoDeVuelo asiento, Vuelo vuelo) {
		if (!vuelo.getAsientos().contains(asiento)){
			asiento.getVuelo().setAerolinea(this);
			vuelo.agregarAsiento(asiento);
			AsientosHome.getInstance().update(asiento);
			VuelosHome.getInstance().update(vuelo);
		}
	}

	private void agregarVueloSiCorresponde(Vuelo vuelo, ArrayList<Vuelo> vuelos) {
		if (!vuelos.contains(vuelo)){
			vuelos.add(vuelo);
		}
		if (!this.vuelos.contains(vuelo)){
			this.vuelos.add(vuelo);
		}
	}
	
	protected Vuelo buscarVuelo(final AsientoDeVuelo asiento) {
		Predicate<Vuelo> predicate = new Predicate<Vuelo>() {
			public boolean apply(Vuelo vuelo) {
				return vuelo.equals(asiento.getVuelo());
			}
		};
				
		return Iterables.find(vuelos, predicate, asiento.getVuelo());
	}
	
	protected AsientoDeVuelo buscarAsiento(final AsientoDeVuelo asientoBuscado, Vuelo vuelo) {
		Predicate<AsientoDeVuelo> predicate = new Predicate<AsientoDeVuelo>() {
	
			public boolean apply(AsientoDeVuelo asiento) {
				return asiento.equals(asientoBuscado);
			}
		};
				
		return Iterables.find(vuelo.getAsientos(), predicate, asientoBuscado);
	}
	
	public void registrarTransaccion(Transaccion transaccion) {
		TransaccionesHome.getInstance().registrar(transaccion);
		
	}
}
